# fuse-ramdisk-fs

For this week’s weekend project, I want to mess around with
[libfuse](https://github.com/libfuse/libfuse/), which is a library that lets
you implement and mount filesystems in userspace.

After reading some of the documentation, I found that basically I need to
declare a [struct](http://libfuse.github.io/doxygen/structfuse__operations.html)
with all the operations that can be called on the mounted file system — and
then just call `fuse_main(argc, argv, op, private_data)` to start
the event loop. Sounds easy, right? Let’s try it.

## Installing FUSE

Since I’m on macOS, I can’t use `libfuse` directly — it’s Linux-only. But,
thankfully there is a port of it called
[OSXFuse](https://github.com/osxfuse/osxfuse) that provides the same
functionality with the same API. I was able to install that rather pain-free
with [homebrew](https://brew.sh/), but it’s easy on Linux as well (using `apt`
on Ubuntu, for example):

    # on macOS
    $ brew tap caskroom/cask
    $ brew cask install osxfuse
    
    # on ubuntu
    $ apt install libfuse-dev

## Create a project

First thing is to just create folder for this project and initialize it as a
git repository.

    $ mkdir fuse-ramdisk-fs
    $ cd fuse-ramdisk-fs
    $ git init

### Build system

A `Makefile` is the perfect build system for this project, since there is just
one code file which has one dependency (`libfuse`). To simplify things,
`pkg-config` can be used to automatically generated the necessary compiler
flags.

###### File Makefile, lines 0–3:

```make
PACKAGES = fuse
CFLAGS  += $(shell pkg-config --cflags $(PACKAGES))
LDFLAGS += $(shell pkg-config --libs $(PACKAGES))
```

Since `make` comes with a lot of implicit rules, we can get away with not
actually telling it what it needs to do. By defining a target `fuse-ramdisk-fs`
and naming the main code file `fuse-ramdisk-fs.c`, it will automatically figure
out what it needs to do.

###### File Makefile, lines 4–5:

```make
fuse-ramdisk-fs:
```

### Initial code

One little thing that bit me when initially playing around with `libfuse` is
that it needs to know which version of the API to use, which means that before
including it, I needed to define `FUSE_USE_VERSION` to something sensible, I
just went ahead with the current version which is `32`, but you may feel free
to use a later version to make use of improvements (but you may have to adapt
your code, in that case).

###### File fuse-ramdisk-fs.c, lines 0–2:

```c
#define FUSE_USE_VERSION 32
#include <fuse.h>
```

And then a simple `main` function will do, since I’ll change this later
anyways.

###### File fuse-ramdisk-fs.c:

```c
int main(int argc, char *argv[])
{
  return 0;
}
```

## Using the FUSE main loop

FUSE needs to run in a main loop to handle the calls necessary for accessing
and manipulating the file system. Since I’m not doing anything complicated,
I’ll just let `libfuse` set up this loop. To do that, I need to create an
instance of `struct fuse_operations`, but I won’t set any of the functions
yet—they will come later.

###### File fuse-ramdisk-fs.c, lines 3–8:

```c
int main(int argc, char *argv[])
{
  struct fuse_operations op = {NULL};
  return fuse_main(argc, argv, &op, NULL);
}
```

At this point, it’s possible to compile everything by running `make` and
running it:

    $ make
    cc -D_FILE_OFFSET_BITS=64 -D_DARWIN_USE_64_BIT_INODE -I/usr/local/include/osxfuse/fuse  -L/usr/local/lib -losxfuse  fuse-ramdisk-fs.c   -o fuse-ramdisk-fs
    
    $ ./fuse-ramdisk-fs -h
    usage: ./fuse-ramdisk-fs mountpoint [options]
    
    general options:
        -o opt,[opt...]        mount options
        -h   --help            print help
        -V   --version         print version
    
    FUSE options:
        -d   -o debug          enable debug output (implies -f)
        -f                     foreground operation
        -s                     disable multi-threaded operation
    
    fuse: no mount point

So to actually use this program, you need to create a folder and run
`./fuse-ramdisk-fs path/to/folder`. However, since there aren’t any operations
defined in `op`, it won’t really do much.

## Implementing operations
